///////////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2011 Gabriel Montagné Láscaris-Comneno and Alberto
//  Brealey-Guzmán.
//
//  Permission is hereby granted, free of charge, to any person obtaining a
//  copy of this software and associated documentation files (the "Software"),
//  to deal in the Software without restriction, including without limitation
//  the rights to use, copy, modify, merge, publish, distribute, sublicense,
//  and/or sell copies of the Software, and to permit persons to whom the
//  Software is furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
//  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
//  DEALINGS IN THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////

package com.rojored.preloaders
{

import flash.events.Event;
import flash.events.ProgressEvent;

/**
 *  Base class for staged preloader display. Implements an animated progress
 *  (with Natzke-esque tweening).
 *
 *  <p>A staged preloader display will display the progress while downloading
 *  the Flex application and can be later reused to display the progress of
 *  the bootstrap or initialization stages.</p>
 *
 *  <p>The class does not display any kind of progress. You must extend the
 *  class and provide handlers for the many progress and completion events to
 *  give feedback to the user about the loading of the application.</p>
 */
public class StagedPreloaderBase extends PreloaderBase
    implements IStagedPreloaderDisplay
{

    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------

    /**
     * Constructor
     */
    public function StagedPreloaderBase()
    {
        super();
        registerHandlers();
    }

    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     *  Because we're easing the movement we need an additional variable to
     *  keep the progress that is being drawn separate from the actual
     *  download progress.
     */
    private var easedProgress:Number = 0;

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  stepProgressTo
    //----------------------------------

    private var _stepProgressTo:Number = 1;

    /**
     *  @inheritDoc
     */
    public function get stepProgressTo():Number
    {
        return _stepProgressTo;
    }

    public function set stepProgressTo(value:Number):void
    {
        _stepProgressTo = value;
    }

    //----------------------------------
    //  stepProgressFrom
    //----------------------------------

    private var _stepProgressFrom:Number = 0;

    /**
     *  @inheritDoc
     */
    public function get stepProgressFrom():Number
    {
        return _stepProgressFrom;
    }

    public function set stepProgressFrom(value:Number):void
    {
        _stepProgressFrom = value;
    }

    //----------------------------------
    //  easingFactor
    //----------------------------------

    private var _easingFactor:Number = 0.7;

    /**
     *  Fraction of the remaining distance to the current progress that will
     *  be covered in the current step. A value of 1 will turn off the easing
     *  effect. The lower the value, the slower the animation will advance as
     *  the progress percentage approaches <code>stepProgressTo</code>
     */
    public function get easingFactor():Number
    {
        return _easingFactor;
    }

    public function set easingFactor(value:Number):void
    {
        _easingFactor = value;
    }

    //----------------------------------
    //  progress
    //----------------------------------

    private var _progress:Number;

    /**
     *  @inheritDoc
     */
    public function get progress():Number
    {
        return _progress;
    }

    public function set progress(value:Number):void
    {
        _progress = value;
    }

    //--------------------------------------------------------------------------
    //
    //  Methods
    //
    //--------------------------------------------------------------------------

    /**
     *  Method called to render the progress in the preloader.
     *
     *  <p>You have to override this method to display progress.</p>
     *
     *  @param fraction Download progress. Number between 0 and 1.
     */
    protected function drawProgress(fraction:Number):void
    {
        // Abstract method
    }

    /**
     * @private
     */
    private function registerHandlers():void
    {
        addEventListener(
            Event.ADDED_TO_STAGE,
            addedToStageHandler,
            false,
            0,
            true
        );

        addEventListener(
            Event.REMOVED_FROM_STAGE,
            removedFromStageHandler,
            false,
            0,
            true
        );
    }

    /**
     * @private
     */
    private function removeHandlers():void
    {
        removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
        removeEventListener(
            Event.REMOVED_FROM_STAGE,
            removedFromStageHandler
        );
    }

    /**
     *  @inheritDoc
     */
    public function startUpdates():void
    {
        addEventListener(
            Event.ENTER_FRAME,
            enterFrameHandler,
            false,
            0,
            true
        );
    }

    /**
     *  @inheritDoc
     */
    public function stopUpdates():void
    {
        removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
    }

    /**
     *  @inheritDoc
     */
    public function clear():void
    {
        stopUpdates();
    }

    //--------------------------------------------------------------------------
    //
    //  Overriden event handlers
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    override protected function progressHandler(event:ProgressEvent):void
    {
        progress = event.bytesLoaded / event.bytesTotal;
    }

    //--------------------------------------------------------------------------
    //
    //  Event handlers
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    private function enterFrameHandler(event:Event):void
    {
        var newProgress:Number =
            easedProgress + (((progress || 0) - easedProgress) * easingFactor);

        // Progress shouldn't go back.
        easedProgress = Math.max(easedProgress, newProgress);

        drawProgress(
            (easedProgress * (stepProgressTo - stepProgressFrom)) + stepProgressFrom
        );
    }

    /**
     *  @private
     */
    private function addedToStageHandler(event:Event):void
    {
        startUpdates();
    }

    /**
     *  @private
     */
    private function removedFromStageHandler(event:Event):void
    {
        stopUpdates();
    }
}
}
